package il.ac.tau.cg.ex3.math.shapes;

import il.ac.tau.cg.ex3.math.Params;
import il.ac.tau.cg.ex3.math.Point;
import il.ac.tau.cg.ex3.math.Vector;
import il.ac.tau.cg.ex3.utils.octree.BoundingBox;

import java.util.SortedSet;
import java.util.TreeSet;

public class Disc extends Shape
{
	private Point center;
	private Vector normal;
	private double radius;
	
	public Disc(Point center, Vector normal, double radius)
	{
		this.center = center;
		this.normal = normal;
		this.radius = radius;
	}
	
	public void setCenter(Point center)
	{
		this.center = center;
	}
	
	public Point getCenter()
	{
		return center;
	}
	
	public void setNormal(Vector normal)
	{
		this.normal = normal;
	}
	
	public Vector getNormal()
	{
		return normal;
	}
	
	public void setRadius(double radius)
	{
		this.radius = radius;
	}
	
	public double getRadius()
	{
		return radius;
	}

	@Override
	public SortedSet<Double> intersect(Point origin, Vector direction)
	{
		Vector v1 = normal.arbitraryOrthogonal().normalize().scale(radius);
		Vector v2 = normal.cross(v1).normalize().scale(radius);
		
		Point p0 = center.add(v1).add(v2);
		Point p1 = p0.add(v1.scale(-2.0));
		Point p2 = p0.add(v2.scale(-2.0));
		
		SortedSet<Double> ints = new Rectangle(p0, p1, p2).intersect(origin, direction);
		SortedSet<Double> result = new TreeSet<Double>();
		
		for (Double t : ints)
		{
			Point p = origin.add(direction.scale(t));
			
			if (center.dist(p) <= radius)
			{
				result.add(t);
			}
		}
		
		return result;
	}

	@Override
	public Vector getNormal(Point p)
	{
		return normal.clone();
	}

	@Override
	public Params getParams(Point p)
	{
		Vector vec = new Vector(center, p).normalize();
		
		Vector ao1 = normal.arbitraryOrthogonal().normalize();
		Vector ao2 = ao1.cross(normal).normalize();
		
		double c = vec.dot(ao1);
		double s = Math.acos(vec.dot(ao2));
		
		double u = p.dist(center) / radius;
		double v = (Math.PI + ((c > 0.0) ? s : -s)) / (2.0 * Math.PI);
		
		u = Math.min(Math.max(u, 0.0), 1.0);
		v = Math.min(Math.max(v, 0.0), 1.0);
		
		return new Params(u, v);
	}

	@Override
	public BoundingBox getBoundingBox()
	{
		Vector v1 = normal.arbitraryOrthogonal().normalize().scale(radius);
		Vector v2 = normal.cross(v1).normalize().scale(radius);
		
		Point p0 = center.add(v1).add(v2);
		Point p1 = p0.add(v1.scale(-2.0));
		Point p2 = p0.add(v2.scale(-2.0));
		
		return new Rectangle(p0, p1, p2).getBoundingBox();
	}
}
